package com.example.spring.config;

import com.example.spring.pojo.BeanDefinition;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>
 *
 * </p>
 *
 * @author gqz
 * @version 1.0
 * @since 2021/10/28:15:56
 */
public class AppContext {

    private Class config;

    //单例池
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
    //beanDefinition对象
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionConcurrentHashMap = new ConcurrentHashMap<>();
    //装有BeanPostProcessor接口的集合
    private List<BeanPostProcessor> postProcessorList=new ArrayList<>();

    public AppContext(Class config) {
        this.config = config;
        //解析配置类
        //ComponentScan注解----->扫描路径----->扫描
        //得到当前类上的注解   getDeclaredAnnotations:忽略从父类继承过来的注解
//        Annotation[] annotations = config.getDeclaredAnnotations();
        if (config.isAnnotationPresent(ComponentScan.class)) {
            scan(config);
            //获取所有的beanDefinition对象
            for (String beanName : beanDefinitionConcurrentHashMap.keySet()) {
                BeanDefinition beanDefinition = beanDefinitionConcurrentHashMap.get(beanName);
                if (beanDefinition.getScope().equals("singleton")) {
                    //创建单例bean
                    Object bean = createBean(beanDefinition,beanName);
                    singletonObjects.put(beanName, bean);
                } else {
                    //创建原型bean
                    Object bean = createBean(beanDefinition,beanName);
                }
            }
        }
    }

    private Object createBean(BeanDefinition beanDefinition,String beanName) {
        Class clazz = beanDefinition.getClazz();
        Object o = null;
        try {
            //初始化
            o = clazz.getDeclaredConstructor().newInstance();
            //依赖注入
            //当前对象中的所有属性
            Field[] fields = clazz.getDeclaredFields();
            for (Field f: fields) {
                if (f.isAnnotationPresent(Autowired.class))
                {
                    String name = f.getName();
                    if (beanDefinitionConcurrentHashMap.containsKey(name))
                    {
                        //方法一:去beanDefinitionConcurrentHashMap获取要注入的实例对象并给属性赋值
                        BeanDefinition bean = beanDefinitionConcurrentHashMap.get(name);
                        if (bean==null)
                        {
                            throw new NullPointerException();
                        }
                        Object o1 = bean.getClazz().newInstance();
                        f.setAccessible(true);
                        //给clazz对象中的f属性去赋值
                        f.set(o,o1);
                        //方法二:通过getBean方法获取要注入的实例对象并给属性赋值
//                        Object bean = getBean(name);
//                        f.setAccessible(true);
//                        f.set(o,bean);
                         //aware回调
                        if (o instanceof BeanNameAware)
                        {
                            ((BeanNameAware)o).setName(beanName);
                        }
                        //初始化前
                        if (!CollectionUtils.isEmpty(postProcessorList))
                        {
                            postProcessorList.forEach(p->{
                                p.postProcessBeforeInitialization(clazz,beanName);
                            });
                        }
                        //初始化
                        if (o instanceof InitializingBean)
                        {
                            ((InitializingBean)o).afterPropertiesSet(beanName);
                        }
                        //初始化后
                        if (!CollectionUtils.isEmpty(postProcessorList))
                        {
                            postProcessorList.forEach(p->{
                                p.postProcessAfterInitialization(clazz,beanName);
                            });
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return o;
    }

    /**
     * 扫描
     *
     * @param clazz 类
     */
    private void scan(Class clazz) {
        //拿到指定的注解
        ComponentScan componentScan = (ComponentScan) config.getAnnotation(ComponentScan.class);
        //拿到注解的属性值
        String path = componentScan.value();
        //扫描
        //Bootstrap--->jre/lib
        //Ext--->jre/ext/lib
        //App--->classpath
        //通过类加载器加载相应的文件
        ClassLoader classLoader = AppContext.class.getClassLoader();
        URL url = classLoader.getResource(path.replace(".", "/"));
        //拿到指定目录
        File file = new File(url.getFile());
        //判断是否是目录
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                if (f.getName().endsWith(".class")) {
                    Class<?> aClass;
                    try {
                        String fileName = path + "." + f.getName();
                        String classPath = fileName.substring(0, fileName.lastIndexOf("."));
                        aClass = classLoader.loadClass(classPath);
                        //判断当前类注解上是否存在Component这个注解
                        if (aClass.isAnnotationPresent(Component.class)) {
                            //表示当前这个类是一个bean
                            Component component = aClass.getDeclaredAnnotation(Component.class);
                            //当前这个bean所对应的名字
                            String beanName = component.value();
                            BeanDefinition beanDefinition = new BeanDefinition();
                            //解析类,判断当前的bean是单例bean,还是原型bean
                            if (aClass.isAnnotationPresent(Scope.class)) {
                                Scope scope = aClass.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scope.value());
                            }
                            //表示当前这个类是个单例
                            else {
                                beanDefinition.setScope("singleton");
                            }

                            //判断当前class对象是否实现了BeanPostProcessor接口
                            if (BeanPostProcessor.class.isAssignableFrom(aClass))
                            {
                                BeanPostProcessor instance = (BeanPostProcessor)aClass.getDeclaredConstructor().newInstance();
                                postProcessorList.add(instance);
                            }

                            beanDefinition.setClazz(aClass);
                            //BeanDefinition
                            beanDefinitionConcurrentHashMap.put(beanName, beanDefinition);
                        }
                    } catch (ClassNotFoundException e) {
                        System.out.println("不是java类文件,无法加载");
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public Object getBean(String beanName) {
        if (beanDefinitionConcurrentHashMap.containsKey(beanName)) {
            BeanDefinition beanDefinition = beanDefinitionConcurrentHashMap.get(beanName);
            String scope = beanDefinition.getScope();
            if (scope.equals("singleton")) {
                Object o = singletonObjects.get(beanName);
                return o;
            } else {
                //创建bean对象
                Object bean = createBean(beanDefinition,beanName);
                return bean;
            }
        } else {
            throw new NullPointerException();
        }
    }
}
